
package cn.zlg.common.springmvc.validate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import cn.zlg.common.datasafety.CannotDeleteDataException;
import cn.zlg.common.springmvc.apiresultproperty.ApiFinalUrl;
import cn.zlg.common.springmvc.apiresultproperty.ApiThirtyFourResult;
import com.alibaba.fastjson.JSON;
import com.joysuch.wwyt.common.resultdto.ApiResultDto;
import com.joysuch.wwyt.licence.exception.LicenceException;
import com.joysuch.wwyt.openapi.constants.ApiCode;
import com.joysuch.wwyt.openapi.constants.ApiStatus;
import com.joysuch.wwyt.openapi.exception.ApiV1ExceptionCodeMsg;
import com.joysuch.wwyt.openapi.result.ApiV1Result;
import com.joysuch.wwyt.repeatsubmit.exception.RepeatSubmitException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
import org.springframework.web.servlet.view.json.MappingJackson2JsonView;

public class ValidationExceptionResolver extends AbstractHandlerExceptionResolver {

    private static final Logger logger = LoggerFactory.getLogger(ValidationExceptionResolver.class);

    // 可替换，生成不同类型的消息格式
    private JsonExceptionResponseBodyBuilder jsonMessageBuilder = new DefaultJsonExceptionResponseBodyBuilder();

    /**
     * API 接口 可替换，生成不同类型的消息格式
     */
    private ApiExceptionResponseBodyBuilder apiExceptionResponseBodyBuilder = new ApiDefaultExceptionResponseBodyBuilder();

    private String errorPage = "/error";

    public ValidationExceptionResolver() {
        // 设置order，在DefaultHandlerExceptionResolver之前执行
        this.setOrder(0);
    }

    /**
     * Handle the case where an argument annotated with {@code @Valid} such as
     * an {@link } or {@link } argument fails validation.
     * <p>
     * 自定义ValidationException 异常处理器
     * 获取到具体的validation 错误信息，并组装CommonResponse，返回给调用方。
     *
     * @param request  current HTTP request
     * @param response current HTTP response
     * @param handler  the executed handler
     * @param ex
     * @return an empty ModelAndView indicating the exception was handled
     * @throws IOException potentially thrown from response.sendError()
     */
    @ResponseBody
    protected ModelAndView handleMethodArgumentNotValidException(BindingResult bindingResult,
                                                                 HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws IOException {

        List<ObjectError> errors = bindingResult.getAllErrors();
        StringBuilder errmsgBF = new StringBuilder();
        for (ObjectError error : errors) {
            String massage = error.getDefaultMessage();
            System.err.println(massage);
            errmsgBF.append(massage);
            errmsgBF.append("||");
        }
        String errmsgString = errmsgBF.toString();
        errmsgString = errmsgString.length() > 2 ? errmsgString.substring(0, errmsgString.length() - 2) : errmsgString;
        logger.error("Validation failed!  " + errmsgString);

        ModelAndView mav = new ModelAndView();
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 400, errmsgString));
        mav.setView(view);

        return mav;
    }

    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
                                              Exception ex) {

        BindingResult bindingResult = null;
        if (ex instanceof MethodArgumentNotValidException) {
            bindingResult = ((MethodArgumentNotValidException) ex).getBindingResult();
        } else if (ex instanceof BindException) {
            bindingResult = ((BindException) ex).getBindingResult();
        } else if (ex instanceof org.apache.shiro.authz.UnauthorizedException) {
            // 其他异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 403, "您没有权限执行此操作"));
            mav.setView(view);
            return mav;
        } else if (ex instanceof IllegalArgumentException) {
            // 其他异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 300, ex.getMessage()));
            mav.setView(view);
            return mav;
        } else if (ex instanceof RepeatSubmitException) {
            // 重复提交异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 700, ex.getMessage()));
            mav.setView(view);
            return mav;
        } else if (ex instanceof CannotDeleteDataException) {
            // 其他异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 303, ex.getMessage()));
            mav.setView(view);
            return mav;
        } else if (ex instanceof ApiV1ExceptionCodeMsg) {
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ApiV1Result apiV1Result = apiExceptionResponseBodyBuilder.apiV1ExceptionCodeMsg(((ApiV1ExceptionCodeMsg) ex).getStatus(),
                    ((ApiV1ExceptionCodeMsg) ex).getCode(), ((ApiV1ExceptionCodeMsg) ex).getMessage());
            view.setAttributesMap(JSON.parseObject(JSON.toJSONString(apiV1Result), Map.class));
            mav.setView(view);
            return mav;
        } else if (ex instanceof HttpRequestMethodNotSupportedException) {
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ApiV1Result apiV1Result = apiExceptionResponseBodyBuilder.apiV1ExceptionCodeMsg(((ApiV1ExceptionCodeMsg) ex).getStatus(),
                    ((ApiV1ExceptionCodeMsg) ex).getCode(), ((ApiV1ExceptionCodeMsg) ex).getMessage());
            view.setAttributesMap(JSON.parseObject(JSON.toJSONString(apiV1Result), Map.class));
            mav.setView(view);
            return mav;
        } else if (ex instanceof HttpMessageNotReadableException) {
            // API 请求格式异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();

            if (request.getRequestURI().startsWith(ApiFinalUrl.API_V1_URL)) {
                ApiResultDto apiResultDto = apiExceptionResponseBodyBuilder.apiTokenFormatExceptionResponse();
                view.setAttributesMap(JSON.parseObject(JSON.toJSONString(apiResultDto), Map.class));
                mav.setView(view);
                return mav;
            }
            return null;
        } else if (ex instanceof LicenceException) {
            // licence 异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            LicenceException licenceException = (LicenceException) ex;
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, licenceException.getCode(), licenceException.getMsg()));
            mav.setView(view);
            return mav;
        }  else {
            // other exception , ignore
            logger.error("", ex);
        }
        // ajax请求
        if (bindingResult != null) {
            System.out.println("参数验证失败");
            // 参数验证错误事件
            try {
                return handleMethodArgumentNotValidException(bindingResult, request, response, handler, ex);
            } catch (IOException e) {
                logger.error("doResolveException: ", e);
                return null;
            }
        } else {
            // 其他异常
            ModelAndView mav = new ModelAndView();
            MappingJackson2JsonView view = new MappingJackson2JsonView();
            ex.printStackTrace();
            //api 版本 异常
            if (request.getRequestURI().startsWith(ApiFinalUrl.API_V1_URL)) {
                return this.commonThreeDotFourException("操作失败,请稍后重试");
            }
            String message = "（错误操作）操作出现了一点小问题，请重新尝试。";
            if (printableErrorMessage(ex)) {
                message = ex.getMessage();
            }
            view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 500, message));
            // view.setAttributesMap(jsonMessageBuilder.build(ex, bindingResult, 500, ex.getMessage()));
            mav.setView(view);
            return mav;
        }
        // if (HttpUtils.isAjaxRequest(request)) {
        //
        // } else {
        // // 非ajax请求
        // ModelAndView mav = new ModelAndView(errorPage);
        // mav.addObject("exception", ex);
        // return mav;
        // }
    }

    private boolean printableErrorMessage(Exception ex) {
        if (ex instanceof IllegalArgumentException) {
            return true;
        }
        return false;
    }

    public JsonExceptionResponseBodyBuilder getJsonMessageBuilder() {
        return jsonMessageBuilder;
    }

    public void setJsonMessageBuilder(JsonExceptionResponseBodyBuilder jsonMessageBuilder) {
        this.jsonMessageBuilder = jsonMessageBuilder;
    }

    /**
     * api 异常
     *
     * @param errMsg
     * @return
     */
    private ModelAndView commonThreeDotFourException(String errMsg) {
        ModelAndView mav = new ModelAndView();
        MappingJackson2JsonView view = new MappingJackson2JsonView();
        List<String> disposeAlarmList = new ArrayList<>();
        disposeAlarmList.add(errMsg);
        view.setAttributesMap(jsonMessageBuilder.apiThirtyFourTokenReturn(new ApiThirtyFourResult(-90, disposeAlarmList, JSON.parse("{}"), null, false)));
        mav.setView(view);
        return mav;
    }
}
